package unimelb.filesync.common.misc;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import unimelb.filesync.common.message.FileSyncRequest;
import unimelb.filesync.common.message.Jsonable;
import unimelb.filesync.common.message.NetworkMessage;
import unimelb.filesync.common.message.PasswordMsg;
import unimelb.filesync.common.message.RequestBlockSizeRole;
import unimelb.filesync.common.message.RequestPublicKey;
import unimelb.filesync.common.message.ResponseBlockSizeRole;
import unimelb.filesync.common.message.ResponsePublicKey;
import unimelb.filesync.common.message.SecretKeyMsg;
import unimelb.filesync.common.message.SequenceCompleteNotification;
import filesync.Instruction;
import filesync.InstructionFactory;

public class JsonConverter {

    private final JSONParser parser = new JSONParser();

    private final InstructionFactory instrFactory = new InstructionFactory();

    @SuppressWarnings("unchecked")
    public String toJson(NetworkMessage msg) {
        final JSONObject jsonObject = new JSONObject();
        jsonObject.put("type", msg.getType());

        final Jsonable content = msg.getContent();
        jsonObject.put("content", content.toJson());

        return jsonObject.toJSONString();
    }

    public NetworkMessage fromJson(String json) {
        try {
            final JSONObject jsonObject = (JSONObject) parser.parse(json);

            final String type = (String) jsonObject.get("type");
            final String contentJson = (String) jsonObject.get("content");

            try {
                NetworkMessage msg = null;
                if (NetworkMessage.TYPE_REQUEST_BLOCK_SIZE_ROLE.equals(type)) {
                    msg = NetworkMessage.createBlockSizeRoleRequestMsg(createBlockSizeRoleRequestContent(contentJson));
                } else if (NetworkMessage.TYPE_RESPONSE_BLOCK_SIZE_ROLE.equals(type)) {
                    msg = NetworkMessage.createBlockSizeRoleResponseMsg(createBlockSizeRoleResponseContent(contentJson));
                } else if (NetworkMessage.TYPE_FILE_SYNC_REQUEST.equals(type) || NetworkMessage.TYPE_NEW_BLOCK_REQUEST.equals(type)) {
                    msg = NetworkMessage.createFileSyncRequestMsg(createFileSyncRequestContent(contentJson));
                } else if (NetworkMessage.TYPE_SEQUENCE_COMPLETE.equals(type)) {
                    msg = NetworkMessage.createSequenceCompleteMsg(createSequenceCompleteContent(contentJson));
                } else if (NetworkMessage.TYPE_REQUEST_PUBLIC_KEY.equals(type)) {
                    msg = NetworkMessage.createPublicKeyRequestMsg(createPublicKeyRequestContent(contentJson));
                } else if (NetworkMessage.TYPE_RESPONSE_PUBLIC_KEY.equals(type)) {
                    msg = NetworkMessage.createPublicKeyResponseMsg(createPublicKeyResponseContent(contentJson));
                } else if (NetworkMessage.TYPE_SECRET_KEY_MSG.equals(type)) {
                    msg = NetworkMessage.createSecretKeyMsg(createSecretKeyMs(contentJson));
                } else if (NetworkMessage.TYPE_PASSWORD_MSG.equals(type)) {
                    msg = NetworkMessage.createPasswordMsg(createPasswordMsg(contentJson));
                }
                return msg;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    private ResponseBlockSizeRole createBlockSizeRoleResponseContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final boolean success = (Boolean) jsonObject.get("success");
        final String errorMsg = (String) jsonObject.get("errorMessage");
        if (success) {
            return new ResponseBlockSizeRole();
        } else {
            return new ResponseBlockSizeRole(errorMsg);
        }
    }

    private RequestBlockSizeRole createBlockSizeRoleRequestContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final long blockSize = (Long) jsonObject.get("blockSize");
        final String role = (String) jsonObject.get("role");

        return new RequestBlockSizeRole(blockSize, role);
    }

    private FileSyncRequest createFileSyncRequestContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final Instruction instruction = instrFactory.FromJSON((String) jsonObject.get("instruction"));
        final long seqId = (Long) jsonObject.get("sequenceId");
        final long instrId = (Long) jsonObject.get("instructionId");

        return new FileSyncRequest(instruction, seqId, instrId);
    }

    private SequenceCompleteNotification createSequenceCompleteContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final long seqId = (Long) jsonObject.get("sequenceId");

        return new SequenceCompleteNotification(seqId);
    }

    private RequestPublicKey createPublicKeyRequestContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final boolean isPublicKeyNeeded = (Boolean) jsonObject.get("isPublicKeyNeeded");

        return new RequestPublicKey(isPublicKeyNeeded);
    }

    private ResponsePublicKey createPublicKeyResponseContent(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final String publicKey = (String) jsonObject.get("publicKey");

        return new ResponsePublicKey(publicKey);
    }

    private SecretKeyMsg createSecretKeyMs(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final String secretKey = (String) jsonObject.get("secretKey");

        return new SecretKeyMsg(secretKey);
    }

    private PasswordMsg createPasswordMsg(String contentJson) throws ParseException {
        final JSONObject jsonObject = (JSONObject) parser.parse(contentJson);

        final String password = (String) jsonObject.get("password");

        return new PasswordMsg(password);
    }
}
